using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Utilities;

namespace Services
{
    public enum BackgroundType
    {
        DefaulsBackground
    }

    //public interface IBackground
    //{
    //    float EarthLife { get; set; }
    //}

    public class Background : DrawableGameComponent
    {
        public static int NumberOf = 6;
        BackgroundType type;        
        Vector3 step;
        public static readonly int MAX_EARTH_LIFE = 15;
        public static int EarthLife = -1;
        /// <summary>
        /// La posizione orizzontale della terra tra 1 destra, 0 centro, -1 sinistra se fuori esce gradualmente. 1 di default
        /// </summary>
        public static float relativeHorizontalPositionX = 1;
        /// <summary>
        /// La posizione verticale della terra tra 1 alto, 0 centro, -1 basso se fuori esce gradualmente. -1 di default
        /// </summary>
        public static float relativeVerticalPositionY = -1;
        private static BasicEffect DefaultEffect;

        public Background(Game game, BackgroundType type)
            : base(game)
        {
            game.Services.AddService(typeof(Background), this);
            this.UpdateOrder = (int)ComponentOrders.BACKGROUND;
            this.DrawOrder = (int)ComponentOrders.BACKGROUND;

            var r = new Random();

            initial_pitch = MathHelper.ToRadians(r.Next(360));
            initial_roll = MathHelper.ToRadians(r.Next(360));
            initial_yaw = MathHelper.ToRadians(r.Next(360));
            
            this.type = type;
        }

        /// Alla chiusura facciamo pulizia dei servizi istanziati
        protected override void Dispose(bool disposing)
        {
            Game.Services.RemoveService(typeof(Background));
            base.Dispose(disposing);
        }

        Matrix Rotation(CubeMapFace cube_face)
        {
            switch (cube_face)
            {
                case CubeMapFace.NegativeX:
                    return Matrix.CreateRotationY(MathHelper.PiOver2);
                case CubeMapFace.NegativeY:
                    return Matrix.CreateRotationZ(MathHelper.Pi) * Matrix.CreateRotationX(MathHelper.PiOver2);
                case CubeMapFace.PositiveZ:
                    return Matrix.CreateRotationY(MathHelper.Pi);
                case CubeMapFace.PositiveX:
                    return Matrix.CreateRotationY(-MathHelper.PiOver2);
                case CubeMapFace.PositiveY:
                    return Matrix.CreateRotationZ(MathHelper.Pi) * Matrix.CreateRotationX(-MathHelper.PiOver2);
                case CubeMapFace.NegativeZ:
                    return Matrix.Identity;
                default:
                    return Matrix.Identity;
            }
        }

        BasicEffect cube_effect;
        List<VertexBuffer> cube_vertices;
        IndexBuffer cube_indices;
        List<Texture2D> cube_textures;
        VertexBuffer space_dust;
        Matrix panoramic_projection;
        float initial_pitch, initial_roll, initial_yaw;
        Model Planet;
        Texture2D Atmosphere;
        Vector3 DefaultEmissiveColor = new Vector3(0.5f, 0.5f, 0.5f);


        protected override void LoadContent()
        {
            Planet = Game.Content.Load<Model>(@"Planets\sphere");
            Atmosphere = Game.Content.Load<Texture2D>(@"Planets\planisphere");

            if (DefaultEffect == null)
            {
                foreach (var mesh in Planet.Meshes)
                {
                    foreach (var part in mesh.MeshParts)
                    {
                        BasicEffect efx = part.Effect as BasicEffect;
                        efx.Texture = Atmosphere;
                        efx.TextureEnabled = true;
                        
                        efx.EmissiveColor = DefaultEmissiveColor;
                        efx.AmbientLightColor = Color.Orange.ToVector3() * 0.1f;

                        efx.DirectionalLight0.DiffuseColor = Color.White.ToVector3();
                        efx.DirectionalLight0.Direction = new Vector3(1, -0.25f, -0.55f);
                        efx.DirectionalLight0.Enabled = true;
                        efx.LightingEnabled = true;
                        efx.PreferPerPixelLighting = true;
                        DefaultEffect = efx.Clone() as BasicEffect;
                    }
                }
            }
            else
            {
                Planet.Meshes[0].MeshParts[0].Effect = DefaultEffect.Clone() as BasicEffect;
            }

            
            step = (DefaultEffect.DiffuseColor + DefaultEmissiveColor) / MAX_EARTH_LIFE;

            cube_indices = new IndexBuffer(GraphicsDevice, IndexElementSize.SixteenBits, 6, BufferUsage.WriteOnly);
            cube_indices.SetData(new short[] { 0, 2, 1, 1, 2, 3 });

            cube_vertices = new List<VertexBuffer>(8);
            cube_textures = new List<Texture2D>(8);

            var cube_faces = new[]{
        CubeMapFace.PositiveX,
        CubeMapFace.NegativeX,
        CubeMapFace.PositiveY,
        CubeMapFace.NegativeY,
        CubeMapFace.PositiveZ,
        CubeMapFace.NegativeZ
      };

            //for_each (var cube_face in cube_faces)
            for (int y = 0; y < cube_faces.Length; y++)
            {
                var cube_face = cube_faces[y];
                cube_textures.Add(Game.Content.Load<Texture2D>(GetBackgroundPath() + cube_face.ToString()));
                var vertices = new[]{
          new VertexPositionColorTexture(Vector3.Left  + Vector3.Up   + Vector3.Backward, Color.White, new Vector2(0.0f, 0.0f)),
          new VertexPositionColorTexture(Vector3.Right + Vector3.Up   + Vector3.Backward, Color.White, new Vector2(1.0f, 0.0f)),
          new VertexPositionColorTexture(Vector3.Left  + Vector3.Down + Vector3.Backward, Color.White, new Vector2(0.0f, 1.0f)),
          new VertexPositionColorTexture(Vector3.Right + Vector3.Down + Vector3.Backward, Color.White, new Vector2(1.0f, 1.0f)),
        };
                for (int i = 0; i < vertices.Length; i++)
                {
                    vertices[i].Position = Vector3.Transform(vertices[i].Position, Rotation(cube_face));
                }
                var vertex_buffer = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColorTexture), 4, BufferUsage.WriteOnly);
                vertex_buffer.SetData(vertices);
                cube_vertices.Add(vertex_buffer);
            }

            cube_effect = new BasicEffect(GraphicsDevice);

            var r = new Random();



            //var space_dust_vertices =
            //  (from x in Enumerable.Range(-5, 10)
            //   from y in Enumerable.Range(-2, 2)
            //   from z in Enumerable.Range(-5, 10)
            //   let p = new VertexPositionColorTexture(
            //       new Vector3((float)r.NextDouble() * 0.5f, (float)r.NextDouble() * 0.5f, (float)r.NextDouble() * 0.5f) * 7.5f +
            //       new Vector3(x, y, z) * 15.0f, Color.White, Vector2.Zero)
            //   from v in new[] { p, new VertexPositionColorTexture(p.Position + Vector3.Up * 0.1f, p.Color, Vector2.Zero) }
            //   select v).ToArray();
            ArrayList<VertexPositionColorTexture> al = new ArrayList<VertexPositionColorTexture>();
            for (int x = -5; x < 5; x++)
            {
                for (int y = -2; y < 0; y++)
                {
                    for (int z = -5; z < 5; z++)
                    {
                        var p = new VertexPositionColorTexture(
                                new Vector3((float)r.NextDouble() * 0.5f, (float)r.NextDouble() * 0.5f, (float)r.NextDouble() * 0.5f) * 7.5f +
                                new Vector3(x, y, z) * 15.0f, Color.White, Vector2.Zero);
                        al.Add(p);
                        al.Add(new VertexPositionColorTexture(p.Position + Vector3.Up * 0.1f, p.Color, Vector2.Zero));
                    }
                }
            }
            var space_dust_vertices = al.ToArray();

            space_dust = new VertexBuffer(GraphicsDevice, typeof(VertexPositionColorTexture), space_dust_vertices.Length, BufferUsage.WriteOnly);
            space_dust.SetData(space_dust_vertices);

            panoramic_projection = Matrix.CreatePerspectiveFieldOfView(1.5f, //2.5f * GraphicsDevice.Viewport.Width / 800.0f,
                              GraphicsDevice.Viewport.Width / (float)GraphicsDevice.Viewport.Height, 0.1f, 100.0f);

            base.LoadContent();
        }


        static double elapsedComponentGameTime;

        public override void Draw(GameTime gameTime)
        {
            elapsedComponentGameTime += gameTime.ElapsedGameTime.TotalSeconds;

            GraphicsDevice.Clear(Color.Black);
            GraphicsDevice.BlendState = BlendState.Opaque;


            var T = (float)elapsedComponentGameTime;
            cube_effect.World =
              Matrix.CreateFromYawPitchRoll(initial_yaw + T * 0.005f, initial_pitch + MathHelper.PiOver2 - 1.0f, initial_roll + MathHelper.Pi + 0.5f - T * 0.025f);
            cube_effect.View = Matrix.CreateLookAt(Vector3.Zero, Vector3.Forward, Vector3.Up);
            cube_effect.Projection = panoramic_projection;
            cube_effect.TextureEnabled = true;
            for (int i = 0; i < cube_vertices.Count; i++)
            {
                cube_effect.Texture = cube_textures[i];
                GraphicsDevice.SetVertexBuffer(cube_vertices[i]);
                GraphicsDevice.Indices = cube_indices;
                cube_effect.CurrentTechnique.Passes[0].Apply();
                GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 4, 0, 2);
            }


            var oldRast = GraphicsDevice.RasterizerState;
            GraphicsDevice.RasterizerState = RasterizerState.CullClockwise;

            var world =
              Matrix.CreateTranslation(-Planet.Meshes[0].BoundingSphere.Center) *
              Matrix.CreateScale(1.0f / Planet.Meshes[0].BoundingSphere.Radius * 2) *
              Matrix.CreateRotationY(-(float)gameTime.TotalGameTime.TotalSeconds / 10.0f);

            var sto = - new Vector3(relativeHorizontalPositionX * 4.166667f, relativeVerticalPositionY * 2.5f, 0);
            var guardo = -new Vector3(relativeHorizontalPositionX * 4.166667f, relativeVerticalPositionY * 2.5f, 1);

            var view = Matrix.CreateLookAt(sto, guardo, Vector3.Up);
            var projection = Matrix.CreateOrthographic(5 * GraphicsDevice.Viewport.AspectRatio, 5, 0.1f, 20);


            var fx = Planet.Meshes[0].MeshParts[0].Effect as BasicEffect;
            if (EarthLife < 0)
            {
                fx = DefaultEffect.Clone() as BasicEffect;
            }
            else
            {

                if (EarthLife > MAX_EARTH_LIFE - (MAX_EARTH_LIFE / 3 - 1))
                {
                    fx.EmissiveColor = step * (EarthLife - (MAX_EARTH_LIFE / 3 * 2));
                }
                else
                {
                    fx.DiffuseColor = step * EarthLife;
                }
            }
            ////{X:-9.109415 Y:5.465649 Z:-0.004765867}

            ////for (int i = 0; i < 100000; i++)
            ////{
            ////    var x = GraphicsDevice.Viewport.Unproject(new Vector3(0, 0, (float)i / 100000f), projection, view, Matrix.Identity);
            ////    if (x.Z < 0.005)
            ////    {
            ////        break;
            ////    }
            ////}
            //var touch_ray = GraphicsDevice.Viewport.GetRay(new Vector2(GraphicsDevice.Viewport.Width-1, GraphicsDevice.Viewport.Height-1), view, projection);
            //var plane = new Plane(Vector3.UnitZ, 0);
            //var dist = touch_ray.Intersects(plane);



            Planet.Draw(world, view, projection);

            GraphicsDevice.RasterizerState = oldRast;

            //bloom.Draw(gameTime);


            //var eye_x = 15.0f * (int)(EyePosition.X / 15.0f);
            //var eye_z = 15.0f * (int)(EyePosition.Z / 15.0f);
            //cube_effect.World = Matrix.CreateTranslation(new Vector3(eye_x, 0.0f, eye_z));
            //cube_effect.TextureEnabled = false;
            //cube_effect.VertexColorEnabled = true;
            //GraphicsDevice.SetVertexBuffer(space_dust);
            //cube_effect.CurrentTechnique.Passes[0].Apply();
            //GraphicsDevice.DrawPrimitives(PrimitiveType.LineList, 0, space_dust.VertexCount / 2);

            base.Draw(gameTime);
        }

        private string GetBackgroundPath()
        {
            //switch (type)
            //{ 
            //    case BackgroundType.Atlantis:
            //        return @"Backgrounds\atlantis\";
            //    case BackgroundType.HopeAndGlory:
            //        return @"Backgrounds\hope_and_glory\";
            //    case BackgroundType.YellowShip:
            //        return @"Backgrounds\yellow_ship\";
            //    case BackgroundType.Starfield:
            //        return @"Backgrounds\";
            //    case BackgroundType.Planets:
            //        return @"Backgrounds\planets\";
            //}

            return "Backgrounds/" + type.ToString() + "/";
        }

        //VertexBuffer sv;
        //IndexBuffer si;
        //VertexDeclaration sd;
        //ContentManager content;
        //TextureCube background_texture;
        //Effect sky_effect;
        //EffectParameter world_par, view_par, projection_par, texture_par;
        //protected override void LoadContent()
        //{
        //  content = Game.Content;

        //  sv = new VertexBuffer(GraphicsDevice, VertexPositionColorTexture.SizeInBytes * 100, BufferUsage.WriteOnly);
        //  si = new IndexBuffer(GraphicsDevice, sizeof(UInt16) * 9 * 9 * 6, BufferUsage.WriteOnly, IndexElementSize.SixteenBits);
        //  sd = new VertexDeclaration(GraphicsDevice, VertexPositionColorTexture.VertexElements);
        //  sv.SetData(Splitter.sphere_vertices(10, 10).ToArray());
        //  si.SetData(Splitter.sphere_indices(10, 10).ToArray());

        //  background_texture = content.Load<TextureCube>(@"Backgrounds\nebula");
        //  sky_effect = content.Load<Effect>(@"Backgrounds\Sky");

        //  texture_par = sky_effect.Parameters["Texture"];
        //  world_par = sky_effect.Parameters["World"];
        //  view_par = sky_effect.Parameters["View"];
        //  projection_par = sky_effect.Parameters["Projection"];

        //  base.LoadContent();
        //}

        //public override void Draw(GameTime gameTime)
        //{
        //  Draw(new BoundingSphere(EyePosition, 1.0f));

        //  base.Draw(gameTime);
        //}

        //void Draw(BoundingSphere volume)
        //{
        //  var world = Matrix.CreateTranslation(volume.Center);
        //  texture_par.SetValue(background_texture);
        //  world_par.SetValue(world);
        //  view_par.SetValue(view);
        //  projection_par.SetValue(projection);
        //  sky_effect.Begin(SaveStateMode.SaveState);
        //  sky_effect.CurrentTechnique.Passes[0].Begin();
        //  GraphicsDevice.VertexDeclaration = sd;
        //  GraphicsDevice.Vertices[0].SetSource(sv, 0, VertexPositionColorTexture.SizeInBytes);
        //  GraphicsDevice.Indices = si;
        //  GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 100, 0, 9 * 9 * 2);
        //  sky_effect.CurrentTechnique.Passes[0].End();
        //  sky_effect.End();
        //}
    }
}